// ChartStyle.cs
// 
// Copyright (C) 2008 Jean-André Santoni
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

using System;
using Cairo;
using Rsvg;
using System.Collections;
using System.Collections.Generic;
using ChartStyle;

namespace DefaultStyle
{
	public class ChartStyle : IChartStyle
	{	
		private static Context ctx;
		private static Hashtable ci;
		string appdir = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);
		
		public void Draw(Context ctxarg, Hashtable ciarg)
		{
			ctx = ctxarg;
			ci = ciarg;
			
			Aspects();
			Conjunctions();
			Houses();
			Planets();
			Zodiac();
		}
		
		private void Houses ()
		{
			Handle svg = new Handle(appdir + "/svg/Houses.svg");
			svg.RenderCairo(ctx);
			
			ctx.SetSourceRGB(0.1333, 0.4745, 0.6706);
			ctx.SelectFontFace("Sans", FontSlant.Normal, FontWeight.Bold);
			ctx.SetFontSize(14);
			string str = Convert.ToString(Convert.ToInt32(((Hashtable)ci["Degrees"])["Asc"]));
			TextExtents ext = ctx.TextExtents(str);
			double x =  30 - ( ext.Width/2 + ext.XBearing);
			double y = 300 - (ext.Height/2 + ext.YBearing);
			ctx.MoveTo(x, y);
			ctx.ShowText(str);
		}
		
		private void Zodiac ()
		{
			double deg = Convert.ToDouble(ci["Ascendant"]);
			
			ctx.Translate(300, 300);
			ctx.Rotate(Deg2Rad(deg));
			ctx.Translate(-300, -300);
			
			Handle svg = new Handle(appdir + "/svg/Zodiac.svg");
			svg.RenderCairo(ctx);
			
			ctx.Translate(300, 300);
			ctx.Rotate(-Deg2Rad(deg));
			ctx.Translate(-300, -300);
		}
		
		private void Planets ()
		{
			double deg, rad;
			string pla, str;
			double old_deg = -1000;
			double distance = 185;
			double x1, x2, x3, x4, y1, y2, y3, y4;
			Handle svg;
			TextExtents ext;
			
			foreach(KeyValuePair<string, double> Item in (List<KeyValuePair<string, double>>) ci["Positions"])
			{
				deg = Convert.ToDouble(Item.Value);
				pla = Convert.ToString(Item.Key);	
				rad = Deg2Rad(deg);
				
				str = Convert.ToString(Math.Round(Convert.ToDouble(((Hashtable) ci["Degrees"])[pla]), 2));
				
				if (Math.Abs(old_deg - deg) < 5)
				{
					distance -= 20;
				}
				else
				{
					distance = 185;
				}
				
				x1 = ( Math.Cos(rad) * 200) + 300;
				y1 = (-Math.Sin(rad) * 200) + 300;
				x2 = ( Math.Cos(rad) * 220) + 300;
				y2 = (-Math.Sin(rad) * 220) + 300;
				x3 = ( Math.Cos(rad) * distance) + 300;
				y3 = (-Math.Sin(rad) * distance) + 300;
				x4 = ( Math.Cos(rad) * (distance - 30)) + 300;
				y4 = (-Math.Sin(rad) * (distance - 30)) + 300;
				
				if (pla == "Node" || pla == "SNode" || pla == "Lilith")
				{
					ctx.SetSourceRGB(0, 0, 0);
				}
				else
				{
					ctx.SetSourceRGB(1, 0, 0);
				}
				ctx.LineWidth = 1;
				ctx.MoveTo(x1, y1);
				ctx.LineTo(x2, y2);
				ctx.Stroke();
				
				svg = new Handle(appdir + "/svg/" + pla + ".svg");
				ctx.Translate(x3 - 10, y3 - 10);
				svg.RenderCairo(ctx);
				ctx.Translate(-x3 + 10, -y3 + 10);
				
				ctx.SelectFontFace("Georgia", FontSlant.Normal, FontWeight.Normal);
				ctx.SetFontSize(8);
				ext = ctx.TextExtents(str);
				x4 -=  ext.Width/2 + ext.XBearing;
				y4 -= ext.Height/2 + ext.YBearing;
				ctx.Translate(x4, y4);
				ctx.ShowText(str);
				ctx.Translate(-x4, -y4);
				
				old_deg = deg;
			}
		}
		
		private void Aspects ()
		{
			double deg1, deg2;
			string pla1, pla2;
			
			foreach(KeyValuePair<string, double> Item1 in (List<KeyValuePair<string, double>>) ci["Positions"])
			{
				deg1 = Convert.ToDouble(Item1.Value);
				pla1 = Convert.ToString(Item1.Key);
				
				foreach(KeyValuePair<string, double> Item2 in (List<KeyValuePair<string, double>>) ci["Positions"])
				{
					deg2 = Convert.ToDouble(Item2.Value);
					pla2 = Convert.ToString(Item2.Key);
					
					if( ! (pla1 == "Node" && pla2 == "SNode") && ! (pla2 == "Node" && pla1 == "SNode"))
					{
						TestAspect(deg1, deg2, 180, 10);
					}

					TestAspect(deg1, deg2, 150, 2);
					TestAspect(deg1, deg2, 120, 8);
					TestAspect(deg1, deg2,  90, 6);
					TestAspect(deg1, deg2,  60, 4);
					TestAspect(deg1, deg2,  30, 1);
				}
			}
		}
		
		private void TestAspect (double deg1, double deg2, int delta, double orb)
		{
			if ((deg1 > (deg2 + delta - orb) && deg1 < (deg2 + delta + orb)) ||
			    (deg1 > (deg2 - delta - orb) && deg1 < (deg2 - delta - orb)) ||
			    (deg1 > (deg2 + 360 + delta - orb) && deg1 < (deg2 + 360 + delta + orb)) ||
			    (deg1 > (deg2 - 360 + delta - orb) && deg1 < (deg2 - 360 + delta + orb)) ||
			    (deg1 > (deg2 + 360 - delta - orb) && deg1 < (deg2 + 360 - delta + orb)) ||
			    (deg1 > (deg2 - 360 - delta - orb) && deg1 < (deg2 - 360 - delta + orb)))
			{
				if (deg1 > deg2)
				{
					switch(delta)
					{
						case 180: ctx.SetSourceRGB(1, 0, 0); break;
						case 150: ctx.SetSourceRGB(0, 1, 0); break;
						case 120: ctx.SetSourceRGB(0, 0, 1); break;
						case  90: ctx.SetSourceRGB(1, 0, 0); break;
						case  60: ctx.SetSourceRGB(0, 0, 1); break;
						case  30: ctx.SetSourceRGB(0, 0, 1); break;
						case   0: ctx.SetSourceRGB(1, 0, 0); break;
					}
					Aspect(deg1, deg2);
				}
			}
		}
		
		private void Aspect (double deg1, double deg2)
		{
			double rad1 = Deg2Rad(deg1);
			double rad2 = Deg2Rad(deg2);
			
			double x1 = ( Math.Cos(rad1) * 75) + 300;
			double y1 = (-Math.Sin(rad1) * 75) + 300;
			double x2 = ( Math.Cos(rad2) * 75) + 300;
			double y2 = (-Math.Sin(rad2) * 75) + 300;
			
			ctx.LineWidth = 1;
			ctx.MoveTo(x1, y1);
			ctx.LineTo(x2, y2);
			ctx.Stroke();
		}
		
		private void Conjunctions ()
		{
			KeyValuePair<string, double> h1 =  new KeyValuePair<string,double>("House  1", 180);
			KeyValuePair<string, double> h4 =  new KeyValuePair<string,double>("House  4", 270);
			KeyValuePair<string, double> h7 =  new KeyValuePair<string,double>("House  7",   0);
			KeyValuePair<string, double> h10 = new KeyValuePair<string,double>("House 10",  90);
			
			((List<KeyValuePair<string, double>>) ci["Positions"]).Add(h1);		
			((List<KeyValuePair<string, double>>) ci["Positions"]).Add(h4);
			((List<KeyValuePair<string, double>>) ci["Positions"]).Add(h7);
			((List<KeyValuePair<string, double>>) ci["Positions"]).Add(h10);

			double deg1, deg2;
			
			foreach(KeyValuePair<string, double> Item1 in (List<KeyValuePair<string, double>>) ci["Positions"])
			{
				deg1 = Convert.ToDouble(Item1.Value);
				
				foreach(KeyValuePair<string, double> Item2 in (List<KeyValuePair<string, double>>) ci["Positions"])
				{
					deg2 = Convert.ToDouble(Item2.Value);
					
					TestConjunction(deg1, deg2, 0, 10);
				}
			}
			
			((List<KeyValuePair<string, double>>) ci["Positions"]).Remove(h1);
			((List<KeyValuePair<string, double>>) ci["Positions"]).Remove(h4);
			((List<KeyValuePair<string, double>>) ci["Positions"]).Remove(h7);
			((List<KeyValuePair<string, double>>) ci["Positions"]).Remove(h10);
		}
		
		private void TestConjunction (double deg1, double deg2, int delta, double orb)
		{
			if ((deg1 > (deg2 + delta - orb) && deg1 < (deg2 + delta + orb)) ||
			    (deg1 > (deg2 - delta - orb) && deg1 < (deg2 - delta - orb)) ||
			    (deg1 > (deg2 + 360 + delta - orb) && deg1 < (deg2 + 360 + delta + orb)) ||
			    (deg1 > (deg2 - 360 + delta - orb) && deg1 < (deg2 - 360 + delta + orb)) ||
			    (deg1 > (deg2 + 360 - delta - orb) && deg1 < (deg2 + 360 - delta + orb)) ||
			    (deg1 > (deg2 - 360 - delta - orb) && deg1 < (deg2 - 360 - delta + orb)))
			{
				if (deg1 > deg2)
				{
					Conjunction(deg1, deg2);
				}
			}
		}
		
		private void Conjunction (double deg1, double deg2)
		{
			double rad1 = Deg2Rad(360 - deg1);
			double rad2 = Deg2Rad(360 - deg2);
			
			ctx.SetSourceRGB(1, 0.8, 0.8);
			ctx.LineWidth = 10;
			
			if (Math.Abs(deg1 - deg2) > 20)
			{
				ctx.ArcNegative(300, 300, 212, rad1, rad2);
			}
			else
			{
				ctx.Arc(300, 300, 212, rad1, rad2);
			}
			
			ctx.Stroke();
		}
		
		private double Deg2Rad (double deg)
		{
			return deg * Math.PI / 180;
		}
	}
}

